<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>彩色线条</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>
<body>

<script type="module">
  // import * as THREE from './js/three.module.js'
  import * as THREE from 'https://cdn.jsdelivr.net/gh/mrdoob/three.js/build/three.module.js'
  import { OrbitControls } from 'https://cdn.jsdelivr.net/gh/mrdoob/three.js/examples/jsm/controls/OrbitControls.js'
  import Stats from 'https://cdn.jsdelivr.net/gh/mrdoob/three.js/examples/jsm/libs/stats.module.js'
  import { GUI } from 'https://cdn.jsdelivr.net/gh/mrdoob/three.js/examples/jsm/libs/dat.gui.module.js'

  class RAF {
    constructor () {
      this.init()
    }
    init () {
      this._timerIdMap = {
        timeout: {},
        interval: {}
      }
    }
    run (type = 'interval', cb, interval = 16.7) {
      const now = Date.now
      let stime = now()
      let etime = stime
      //创建Symbol类型作为key值，保证返回值的唯一性，用于清除定时器使用
      const timerSymbol = Symbol()
      const loop = () => {
        this.setIdMap(timerSymbol, type, loop)
        etime = now()
        if (etime - stime >= interval) {
          if (type === 'interval') {
            stime = now()
            etime = stime
          }
          cb()
          type === 'timeout' && this.clearTimeout(timerSymbol)
        }
      }
      this.setIdMap(timerSymbol, type, loop)
      return timerSymbol // 返回Symbol保证每次调用setTimeout/setInterval返回值的唯一性
    }
    setIdMap (timerSymbol, type, loop) {
      const id = requestAnimationFrame(loop)
      this._timerIdMap[type][timerSymbol] = id
    }
    setTimeout (cb, interval) {  // 实现setTimeout 功能
      return this.run('timeout', cb, interval)
    }
    clearTimeout (timer) {
      cancelAnimationFrame(this._timerIdMap.timeout[timer])
    }
    setInterval (cb, interval) { // 实现setInterval功能
      return this.run('interval', cb, interval)
    }
    clearInterval (timer) {
      cancelAnimationFrame(this._timerIdMap.interval[timer])
    }
  }
  var raf = new RAF()
  let url = 'https://cdn.jsdelivr.net/gh/mrdoob/three.js/examples/'
  let spotLight,
    scene,
    analyser,
    audio,
    renderer,
    camera,
    group1,
    group2,
    pointss = [],
    pointNum = 69,
    lineNum = 100
  function init () {
    scene = new THREE.Scene()
    group1 = new THREE.Group()
    for (var i = 0; i < lineNum; i++) {
      let sss = - i * 1.5
      if(i>lineNum/2){
        sss = -i * 0.8
      }
      const curve = new THREE.EllipseCurve(
        0, 0,            // ax, aY
        100 + sss, 10,           // xRadius, yRadius
        // 100, 25,           // xRadius, yRadius
        0, 2 * Math.PI,  // aStartAngle, aEndAngle
        false,            // aClockwise
        0                 // aRotation
      )
      const points = curve.getPoints(pointNum)
      pointss.push(points)
      const geometry = new THREE.BufferGeometry().setFromPoints([])
      const materialLine = new THREE.LineBasicMaterial({ color: 'yellow' })
      const ellipse = new THREE.Line(geometry, materialLine)
      ellipse.rotateZ(Math.PI * 0.012 * i * -1)
      ellipse.rotateX(Math.PI * 0.012 * i)
      group1.add(ellipse)
    }
    // group1.translateY(40)
    group1.translateY(60)
    scene.add(group1)
    const planeGeometry = new THREE.PlaneGeometry(1000, 1000)
    const material = new THREE.MeshPhongMaterial({
      color: 0x222222,
      ambient: 0x555555,
      specular: 0xdddddd,
      shininess: 5,
      reflectivity: 2
    })
    material.side = THREE.DoubleSide
    const plane = new THREE.Mesh(planeGeometry, material)
    plane.rotation.x = -0.5 * Math.PI
    plane.position.x = 15
    plane.position.y = 0
    plane.position.z = 0
    plane.receiveShadow = true
    scene.add(plane)
    spotLight = new THREE.SpotLight('#fff')
    spotLight.position.set(-50, 50, 0)
    // spotLight.angle = Math.PI / 4;
    spotLight.penumbra = 0.5
    spotLight.decay = 2
    spotLight.distance = 200
    scene.add(spotLight)
    const ambientLight = new THREE.AmbientLight('#0c0c0c')
    scene.add(ambientLight)
    const directionalLight = new THREE.DirectionalLight('#fff', 0.7)
    directionalLight.castShadow = true
    directionalLight.position.set(0, 2, 5)
    scene.add(directionalLight)
    const spotLightHelper = new THREE.SpotLightHelper(spotLight)
    const AxesHelper = new THREE.AxesHelper(50)
    // scene.add(AxesHelper)
    // scene.add(spotLightHelper)
    /**
     * 相机设置
     */
    var width = window.innerWidth //窗口宽度
    var height = window.innerHeight //窗口高度
    var k = width / height //窗口宽高比
    var s = 150 //三维场景显示范围控制系数，系数越大，显示的范围越大
    //创建相机对象
    camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
    camera.position.x = 10
    camera.position.y = 100
    camera.position.z = 400
    camera.lookAt(scene.position) //设置相机方向(指向的场景对象)
    /**
     * 创建渲染器对象
     */
    renderer = new THREE.WebGLRenderer({ antialias: true })
    renderer.setSize(width, height) //设置渲染区域尺寸
    renderer.setClearColor('#212121', 1) //设置背景颜色
    document.body.appendChild(renderer.domElement) //body元素中插入canvas对象
    //创建控件对象  相机对象camera作为参数   控件可以监听鼠标的变化，改变相机对象的属性
    var controls = new OrbitControls(camera, renderer.domElement)
    // controls.minDistance = 100;
    // controls.maxDistance = 300;
    controls.maxPolarAngle = 0.4 * Math.PI
    //监听鼠标事件，触发渲染函数，更新canvas画布渲染效果
    controls.addEventListener('change', render)
    renderAnimation()
    console.log(scene)
  }
  // 渲染函数
  let i = 0
  let j = 0
  let p = []
  function render () {
    if(i<pointNum){
      if(j<lineNum){
        p.push( pointss[j][i])
        group1.children[j].geometry.setFromPoints(p)
        group1.children[j].material.color.r = Math.random()
      }
    }else {
      i = -1
      j++
      p = []
    }
    i++
    if(j>=20){
      group1.rotateY(0.01)
    }
    if(j===lineNum){
      console.log('已完成绘制')
    }
    renderer.render(scene, camera) //执行渲染操作
  }
  function renderAnimation () {
    raf.setInterval(() => {
      render()
    }, 0)
  }
  init()
</script>
</body>
</html>
